package com.cq.hd.order.service.impl;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.cq.hd.common.constant.MsgConstant;
import com.cq.hd.common.entity.mq.NotifyMsgSendDto;
import com.cq.hd.common.enums.biz.*;
import com.cq.hd.common.response.Throw;
import com.cq.hd.common.utils.LocalDateUtil;
import com.cq.hd.order.mapper.TbOrderItemMapper;
import com.cq.hd.order.mapper.TbOrderMapper;
import com.cq.hd.order.mapper.TbOrderRefundMapper;
import com.cq.hd.order.mq.provider.NotifyMsgProvider;
import com.cq.hd.order.param.OrderNumSumParam;
import com.cq.hd.order.po.TbOrderItemPo;
import com.cq.hd.order.po.TbOrderPo;
import com.cq.hd.order.po.TbOrderRefundPo;
import com.cq.hd.order.service.OrderStepPriceCashbackService;
import com.cq.hd.product.api.TbActiveStepPriceApi;
import com.cq.hd.product.api.vo.ActiveStepPriceVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.interceptor.DefaultTransactionAttribute;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * 阶梯价返现service
 *
 * @author much
 * @since 2024/05/27 21:20
 */
@Slf4j
@Service
public class OrderStepPriceCashbackServiceImpl implements OrderStepPriceCashbackService {

    // key=活动id, value=活动有效报名数，即活动报名结束时间之前付款成功 & 没有取消的订单（之后取消的不管）
    private final static Map<Long, Integer> ACTIVE_VALID_ENROLL_NUM = new ConcurrentHashMap<>();

    @Resource
    private PlatformTransactionManager transactionManager;
    @Resource
    private TbActiveStepPriceApi activeStepPriceApi;
    @Resource
    private TbOrderMapper orderMapper;
    @Resource
    private TbOrderItemMapper orderItemMapper;
    @Resource
    private TbOrderRefundMapper orderRefundMapper;
    @Resource
    private NotifyMsgProvider notifyMsgProvider;

    // 暂时没用
    @Override
    public void cashbackTask() {
        List<TbOrderPo> orderList = queryWaitCashbackOrders();
        orderList.forEach(this::cashback);
    }

    // 暂时没用
    @Override
    public void cashback(TbOrderPo order) {
        if (!ActiveTypeEnum.GROUP_ENROLL.getValue().equals(order.getOrderType())) {
            return;
        }
        // 没开启阶梯价则不处理
        if (!StepPriceStateEnum.YES.getValue().equals(order.getStepPriceState())) {
            return;
        }
        // 只有全核销才能返现
        if (!OrderStateEnum.VERIFIED.getValue().equals(order.getOrderState())) {
            return;
        }

        BigDecimal returnAmt = calculateReturnAmt(order);
        if (returnAmt.compareTo(BigDecimal.ZERO) > 0) {
            List<TbOrderItemPo> orderItems = queryOrderItems(order.getId());
            performReturnAmtTransaction(order, returnAmt, orderItems);
        }
    }

    /**
     * 暂时没用
     *
     * 子单核销触发执行返现逻辑
     *
     * @param orderItemId 子订单id
     */
    @Override
    public void applyCashbackWhenVerified(Long orderItemId) {
        TbOrderItemPo orderItemPo = orderItemMapper.selectById(orderItemId);
        // 非核销状态不处理
//        if (!SubOrderStateEnum.VERIFIED.getValue().equals(orderItemPo.getSubOrderState())) {
//            return;
//        }
        // 已返现，不处理
        if (orderItemPo.getReturnAmt() != null && orderItemPo.getReturnAmt().compareTo(BigDecimal.ZERO) > 0) {
            return;
        }
        triggerParentOrderCashback(orderItemPo.getOrderId(), null);
    }

    /**
     * 活动满员返现
     *
     * @param orderId 活动id
     * @param enrolledNum 活动满员统计的有效订单数量
     */
    @Override
    public void cashbackWhenActiveFull(Long orderId, Integer enrolledNum) {
        triggerParentOrderCashback(orderId, enrolledNum);
    }

    /**
     * 触发父单返现逻辑
     *
     * @param orderId 父订单id
     */
    private void triggerParentOrderCashback(Long orderId, Integer enrolledNum) {
        TbOrderPo order = orderMapper.selectById(orderId);
        // 需要返现的订单类型
        List<Integer> cashbackOrderTypes = Arrays.asList(
                ActiveTypeEnum.ENROLL.getValue(), ActiveTypeEnum.GROUP_ENROLL.getValue());
        if (!cashbackOrderTypes.contains(order.getOrderType())) {
            return;
        }
        // 没开启阶梯价则不处理
        if (!StepPriceStateEnum.YES.getValue().equals(order.getStepPriceState())) {
            return;
        }

        BigDecimal returnAmt = calculateReturnAmt(order);
        if (returnAmt.compareTo(BigDecimal.ZERO) > 0) {
            List<TbOrderItemPo> orderItems = queryOrderItems(order.getId());
            performReturnAmtTransaction(order, returnAmt, orderItems);
            // 发送通知
            if (enrolledNum != null) {
                sendActiveCashbackNoticeWhenStepFull(order, returnAmt, enrolledNum);
            }
        }
    }

    // 查询待返现订单
    private List<TbOrderPo> queryWaitCashbackOrders() {
        DateTime endTime = DateUtil.beginOfDay(new Date());
        DateTime startTime = DateUtil.beginOfMonth(DateUtil.offsetDay(endTime, -3));
        return orderMapper.selectWaitCashbackOrders(DateUtil.format(startTime, "yyyy-MM-dd HH:mm:ss"),
                DateUtil.format(endTime, "yyyy-MM-dd HH:mm:ss"));
    }

    private List<TbOrderItemPo> queryOrderItems(Long orderId) {
        LambdaQueryWrapper<TbOrderItemPo> queryWrapper = Wrappers.<TbOrderItemPo>lambdaQuery()
                .eq(TbOrderItemPo::getOrderId, orderId)
                .eq(TbOrderItemPo::getDelState, DeleteStatusEnum.NORMAL.getValue());
        return orderItemMapper.selectList(queryWrapper);
    }

    private BigDecimal calculateReturnAmt(TbOrderPo order) {
        // 活动有效报名数
        Integer activeValidEnrollNum = getActiveValidEnrollNum(order.getActiveId(), order.getEndTime());
        // 活动阶梯价配置
        List<ActiveStepPriceVo> activeStepPriceSettings = activeStepPriceApi.listByActiveIds(Collections.singletonList(order.getActiveId())).unpack();
        // 根据报名人数升序
        List<ActiveStepPriceVo> sortedActiveStepPriceSettings = activeStepPriceSettings.stream()
                .sorted(Comparator.comparing(ActiveStepPriceVo::getEnrollNum))
                .collect(Collectors.toList());

        BigDecimal returnAmt = BigDecimal.ZERO;
        for (ActiveStepPriceVo activeStepPriceVo : sortedActiveStepPriceSettings) {
            BigDecimal enrollPrice = activeStepPriceVo.getEnrollPrice();
            if (activeValidEnrollNum >= activeStepPriceVo.getEnrollNum()) {
                returnAmt = order.getActivePrice().subtract(enrollPrice)
                        .multiply(BigDecimal.valueOf(order.getOrderNum()));
            }
        }

        return returnAmt;
    }
    
    private void performReturnAmtTransaction(TbOrderPo order, BigDecimal returnAmt, List<TbOrderItemPo> orderItems) {
        TransactionStatus transaction = transactionManager.getTransaction(new DefaultTransactionAttribute());
        try {
            // 更新父订单的返现金额
            orderMapper.updateReturnAmt(order.getId(), returnAmt);
            // 更新子订单的返现金额，并扣除本金
            distributeOrderItemsCashback(returnAmt, orderItems);
            // 返现记录
            saveOrderRefundRecord(order, returnAmt);

            transactionManager.commit(transaction);
        } catch (Exception e) {
            transactionManager.rollback(transaction);
            log.error(e.getMessage(), e);
        }
    }


    private void sendActiveCashbackNoticeWhenStepFull(TbOrderPo order, BigDecimal returnAmt, Integer orderSize) {
        NotifyMsgSendDto notifyMsgSendDto = new NotifyMsgSendDto();
        notifyMsgSendDto.setUserId(order.getUserId());
        notifyMsgSendDto.setMsgState(NotifyMsgStateEnum.NO_READ.getValue());
        notifyMsgSendDto.setRoleType(RoleTypeEnum.BUSINESS.getValue());
        notifyMsgSendDto.setMsgType(NotifyMsgTypeEnum.USER_ACTIVE_ORDER_RESULT.getValue());
        notifyMsgSendDto.setContent(String.format(MsgConstant.USER_ACTIVE_ORDER_RESULT_STEP_FULL, order.getActiveTitle(),
                orderSize,
                returnAmt.toString()));
        notifyMsgProvider.sendMsg(notifyMsgSendDto);
    }

    /**
     * 保存订单返现记录
     *
     * @param order 订单记录
     * @param returnAmt 返现金额
     */
    private void saveOrderRefundRecord(TbOrderPo order, BigDecimal returnAmt) {
        Integer count = orderRefundMapper.selectCount(new QueryWrapper<TbOrderRefundPo>().eq("del_state", 0)
                .eq("refund_type", RefundTypeEnum.STEP_PRICE.getValue())
                .eq("user_id", order.getUserId())
                .eq("order_id", order.getId()));
        if (count > 0) {
            Throw.isBusinessException("订单返现已存在，orderId=" + order.getId());
        }
        TbOrderRefundPo tbOrderRefundPo = new TbOrderRefundPo();
        tbOrderRefundPo.setBusinessId(order.getBusinessId());
        tbOrderRefundPo.setUserId(order.getUserId());
        tbOrderRefundPo.setOrderId(order.getId());
        // 退款类型(退款类型(1-用户申请退款，2-核销过期自动退,3-报名活动未满员自动退，4-阶梯价格退还))
        tbOrderRefundPo.setRefundType(RefundTypeEnum.STEP_PRICE.getValue());
        tbOrderRefundPo.setOrgReqDate(order.getHfReqDate());
        tbOrderRefundPo.setOrgReqSeqId(order.getHfReqSeqId());
        tbOrderRefundPo.setRefundAmt(returnAmt);
        // 退款交易状态(1-待处理，2-处理中，3-成功，4-失败)
        tbOrderRefundPo.setRefundState(OrderRefundStateEnum.WAIT.getValue());
        tbOrderRefundPo.setRefundApplyTime(LocalDateUtil.getNowLocalDateTime());

        int insert = orderRefundMapper.insert(tbOrderRefundPo);
        if (insert == 0) {
            log.error("阶梯价格退还失败，添加退款记录失败，orderId：{}", order.getId());
            Throw.isBusinessException("阶梯价格退还失败，添加退款记录失败，orderId：" + order.getId());
        }
    }

    // 计算子单均摊的返现额度
    private void distributeOrderItemsCashback(BigDecimal totalReturnAmt, List<TbOrderItemPo> orderItems) {
        // 商品总金额
        BigDecimal totalGoodPrice = orderItems.stream()
                .map(TbOrderItemPo::getGoodsPrice)
                .reduce(BigDecimal.ZERO, BigDecimal::add);

        // 分配返现金额
        BigDecimal distributedCashback = BigDecimal.ZERO;
        for (int i=0; i<orderItems.size()-1; i++) {
            TbOrderItemPo orderItem = orderItems.get(i);
            BigDecimal proportion = orderItem.getGoodsPrice().divide(totalGoodPrice, 10, RoundingMode.HALF_UP);
            BigDecimal cashbackAmount = proportion.multiply(totalReturnAmt).setScale(2, RoundingMode.HALF_UP);
            orderItem.setReturnAmt(cashbackAmount);
            // 更新子单返现金额和本金
            updateOrderItemReturnAmt(orderItem, cashbackAmount);
            distributedCashback = distributedCashback.add(cashbackAmount);
        }
        // 最后一个子订单的返现金额
        TbOrderItemPo lastOrderItem = orderItems.get(orderItems.size() - 1);
        lastOrderItem.setReturnAmt(totalReturnAmt.subtract(distributedCashback));
    }

    private BigDecimal updateOrderItemReturnAmt(TbOrderItemPo orderItem, BigDecimal cashbackAmount) {
        BigDecimal originalCashbackAmount = cashbackAmount;
        BigDecimal merchantAmt = orderItem.getMerchantAmt();
        BigDecimal newMerchantAmt = merchantAmt.subtract(cashbackAmount);
        // 如果本金小于可返金额，则能返多少就返多少
        if (newMerchantAmt.compareTo(BigDecimal.ZERO) < 0) {
            newMerchantAmt = BigDecimal.ZERO;
            cashbackAmount = merchantAmt;
        }
        TbOrderItemPo orderItemUpdater = new TbOrderItemPo();
        orderItemUpdater.setId(orderItem.getId());
        orderItemUpdater.setEstimatedReturnAmt(originalCashbackAmount);
        orderItemUpdater.setReturnAmt(cashbackAmount);
        orderItemUpdater.setMerchantAmt(newMerchantAmt);
        orderItemUpdater.setUpdateTime(LocalDateTime.now());
        orderItemMapper.updateById(orderItemUpdater);

        return cashbackAmount;
    }

    // 活动有效报名数
    private Integer getActiveValidEnrollNum(Long activeId, LocalDateTime endTime) {
        Integer enrollNum = ACTIVE_VALID_ENROLL_NUM.get(activeId);
        if (enrollNum != null) {
            return enrollNum;
        }
        // 没取消的订单
        OrderNumSumParam validOrderNumParam = new OrderNumSumParam();
        validOrderNumParam.setOrderStates(Arrays.asList(OrderStateEnum.WAIT_VERIFY.getValue(), OrderStateEnum.VERIFYING.getValue(),
                OrderStateEnum.FINISH.getValue(), OrderStateEnum.VERIFIED.getValue()));
        validOrderNumParam.setActiveId(activeId);
        Integer validOrderNum = orderMapper.sumOrderNum(validOrderNumParam);
        // 活动报名结束之后取消的订单
        OrderNumSumParam cancelOrderNumParam = new OrderNumSumParam();
        cancelOrderNumParam.setOrderStates(Collections.singletonList(OrderStateEnum.CANCEL.getValue()));
        cancelOrderNumParam.setStartCancelTime(endTime);
        Integer cancelOrderNum = orderMapper.sumOrderNum(cancelOrderNumParam);

        Integer totalNum = Optional.ofNullable(validOrderNum).orElse(0)
                + Optional.ofNullable(cancelOrderNum).orElse(0);
        ACTIVE_VALID_ENROLL_NUM.put(activeId, totalNum);
        return totalNum;
    }

}
